#pragma once

#include<iostream>
#include<pthread.h>
#include<queue>
#include<unistd.h>

#define defalutnum 20
using namespace std;

template<class T>
class BlockQueue
{
public:
    BlockQueue(int maxcap = defalutnum)
    {
        maxcap_ = maxcap;
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&c_cond, nullptr);
        pthread_cond_init(&p_cond, nullptr);
        low_water = maxcap_ / 3;
        high_water = maxcap_ * 2 / 3;
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&c_cond);
        pthread_cond_destroy(&p_cond);
    }

    void push(const T& x)
    {
        pthread_mutex_lock(&mutex_);
        if (high_water <= q.size())
        {
            pthread_cond_wait(&p_cond, &mutex_);
        }

        q.push(x);

        pthread_cond_signal(&c_cond);
        pthread_mutex_unlock(&mutex_);
    }

    T pop()
    {
        pthread_mutex_lock(&mutex_);
        if (low_water >= q.size())
        {
            pthread_cond_wait(&c_cond, &mutex_);
        }
        T t = q.front();
        q.pop();
        pthread_cond_signal(&p_cond);
        pthread_mutex_unlock(&mutex_);
        return t;
    }


private:
    pthread_mutex_t mutex_;
    pthread_cond_t c_cond;
    pthread_cond_t p_cond;
    queue<T> q;

    int maxcap_;

    int low_water;
    int high_water;

};